#include "bsp_dht11.h"

// #define BSP_DHT11_DEBUG_LOG_OPEN // 开启调试信息

#define BSP_DHT11_LOG "[bsp_dht11]"

pin_t g_dht11_pin = GPIO_00;

void my_udelay(uint16_t us)
{
	for (volatile uint16_t i = 0; i < us; i++)
	{
		volatile uint16_t j = 40;
		while (j--)
			;
	}
}

/**
 * 函数功能: 从DHT11读取一个字节，MSB先行
 * 输入参数: 无
 * 返 回 值: 无
 * 说    明：无
 */

/**
 * @brief  从DHT11读取一个字节，MSB先行。
 * @retval 读取到的DHT11数据 成功。
 * @retval ERRCODE_FAIL 失败。
 */
static uint8_t DHT11_ReadByte(void)
{
	uint8_t i, temp = 0;
	uint16_t timeout = 0;

	// 关闭任务调度，防止读取过程中被打断
	osal_kthread_lock();

	for (i = 0; i < 8; i++)
	{
		/*每bit以50us低电平标置开始，轮询直到从机发出 的50us 低电平 结束*/
		while (uapi_gpio_get_val(g_dht11_pin) == GPIO_LEVEL_LOW)
			;

		/*DHT11 以26~28us的高电平表示“0”，以70us高电平表示“1”，
		 *通过检测 x us后的电平即可区别这两个状 ，x 即下面的延时
		 */
		my_udelay(35); // 延时x us 这个延时需要大于数据0持续的时间即可

		if (uapi_gpio_get_val(g_dht11_pin) == GPIO_LEVEL_HIGH) /* x us后仍为高电平表示数据“1” */
		{
			/* 等待数据1的高电平结束 */
			timeout = 0;
			while (uapi_gpio_get_val(g_dht11_pin) == GPIO_LEVEL_HIGH) //;
			{
				timeout++;
				if (timeout > 200)
				{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
					osal_printk("%s DHT11 Data receiving times out.[2]\r\n", BSP_DHT11_LOG);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

					osal_kthread_unlock();
					return (uint8_t)ERRCODE_FAIL;
				}
				my_udelay(1);
			}

			temp |= (uint8_t)(0x01 << (7 - i)); // 把第7-i位置1，MSB先行
		}
		else // x us后为低电平表示数据“0”
		{
			temp &= (uint8_t) ~(0x01 << (7 - i)); // 把第7-i位置0，MSB先行
		}
	}

	osal_kthread_unlock();
	return temp;
}

/**
 * @brief  初始化DHT11设备。
 * @param  [in] pin DHT11设备使用的GPIO引脚， 参考 @ref pin_t 。
 * @retval ERRCODE_SUCC 成功。
 * @retval Other        失败，参考 @ref errcode_t 。
 */
errcode_t bsp_dht11_init(pin_t pin)
{
	errcode_t ret;

	// 保存引脚
	g_dht11_pin = pin;

	// 设置引脚模式
	if (g_dht11_pin == GPIO_04)
	{
		ret = uapi_pin_set_mode(g_dht11_pin, PIN_MODE_2);
	}
	else if (g_dht11_pin == GPIO_05)
	{
		ret = uapi_pin_set_mode(g_dht11_pin, PIN_MODE_4);
	}
	else
	{
		ret = uapi_pin_set_mode(g_dht11_pin, PIN_MODE_0);
	}
	if (ret != ERRCODE_SUCC)
	{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
		osal_printk("%s: set beep pin mode failed, ret = %d\n", BSP_DHT11_LOG, ret);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

		return ret;
	}

	// 设置引脚方向
	ret = uapi_gpio_set_dir(g_dht11_pin, GPIO_DIRECTION_OUTPUT);
	if (ret != ERRCODE_SUCC)
	{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
		osal_printk("%s: set beep pin direction failed, ret = %d\n", BSP_DHT11_LOG, ret);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

		return ret;
	}

	// 设置引脚上拉
	ret = uapi_pin_set_pull(g_dht11_pin, PIN_PULL_TYPE_UP);
	if (ret != ERRCODE_SUCC)
	{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
		osal_printk("%s: set beep pin pull failed, ret = %d\n", BSP_DHT11_LOG, ret);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

		return ret;
	}

	// 设置引脚值
	ret = uapi_gpio_set_val(g_dht11_pin, GPIO_LEVEL_HIGH);
	if (ret != ERRCODE_SUCC)
	{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
		osal_printk("%s: set beep pin value failed, ret = %d\n", BSP_DHT11_LOG, ret);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

		return ret;
	}

	return ERRCODE_SUCC;
}

/**
 * @brief  DHT11读取温湿度数据。
 * @param  [out] DHT11_Data 温湿度数据结构体。 参考 @ref DHT11_Data_TypeDef 。
 * @retval ERRCODE_SUCC 成功。
 * @retval Other        失败，参考 @ref errcode_t 。
 */
errcode_t bsp_dht11_read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
	uint8_t temp;
	uint16_t humi_temp;
	uint16_t timeout = 0;

	// 关闭任务调度器

	/*输出模式*/
	uapi_gpio_set_dir(g_dht11_pin, GPIO_DIRECTION_OUTPUT);

	/*主机拉低*/
	uapi_gpio_set_val(g_dht11_pin, GPIO_LEVEL_LOW);
	/*延时18ms*/
	osal_mdelay(18);
	/*总线拉高 主机延时30us*/
	uapi_gpio_set_val(g_dht11_pin, GPIO_LEVEL_HIGH);

	/*主机设为输入 判断从机响应信号*/
	uapi_gpio_set_dir(g_dht11_pin, GPIO_DIRECTION_INPUT);
	// uapi_pin_set_pull(DHT11_GPIO_PIN, PIN_PULL_TYPE_STRONG_UP);

	my_udelay(30);

	/*判断从机是否有低电平响应信号 如不响应则跳出，响应则向下运行*/
	timeout = 0;
	while (uapi_gpio_get_val(g_dht11_pin) == GPIO_LEVEL_HIGH)
	{
		timeout++;
		if (timeout > 200)
		{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
			osal_printk("%s The DHT11 starts abnormally.[1]\r\n", BSP_DHT11_LOG);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

			return ERRCODE_FAIL;
		}
		my_udelay(1);
	}

	/*轮询直到从机发出 的80us 低电平 响应信号结束*/
	timeout = 0;
	while (uapi_gpio_get_val(g_dht11_pin) == GPIO_LEVEL_LOW)
	{
		timeout++;
		if (timeout > 200)
		{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
			osal_printk("%s The DHT11 starts abnormally.[2]\r\n", BSP_DHT11_LOG);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

			return ERRCODE_FAIL;
		}
		my_udelay(1);
	}

	/*轮询直到从机发出的 80us 高电平 标置信号结束*/
	timeout = 0;
	while (uapi_gpio_get_val(g_dht11_pin) == GPIO_LEVEL_HIGH)
	{
		timeout++;
		if (timeout > 200)
		{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
			osal_printk("%s The DHT11 starts abnormally.[3]\r\n", BSP_DHT11_LOG);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

			return ERRCODE_FAIL;
		}
		my_udelay(1);
	}

	/*开始接收数据*/
	DHT11_Data->humi_high8bit = DHT11_ReadByte();
	DHT11_Data->humi_low8bit = DHT11_ReadByte();
	DHT11_Data->temp_high8bit = DHT11_ReadByte();
	DHT11_Data->temp_low8bit = DHT11_ReadByte();
	DHT11_Data->check_sum = DHT11_ReadByte();

#ifdef BSP_DHT11_DEBUG_LOG_OPEN
	osal_printk("%s DHT11_Data->humi_high8bit=%X\r\n", BSP_DHT11_LOG, DHT11_Data->humi_high8bit);
	osal_printk("%s DHT11_Data->humi_low8bit=%X\r\n", BSP_DHT11_LOG, DHT11_Data->humi_low8bit);
	osal_printk("%s DHT11_Data->temp_high8bit=%X\r\n", BSP_DHT11_LOG, DHT11_Data->temp_high8bit);
	osal_printk("%s DHT11_Data->temp_low8bit=%X\r\n", BSP_DHT11_LOG, DHT11_Data->temp_low8bit);
	osal_printk("%s DHT11_Data->check_sum=%X\r\n", BSP_DHT11_LOG, DHT11_Data->check_sum);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

	/*读取结束，引脚改为输出模式*/
	uapi_gpio_set_dir(g_dht11_pin, GPIO_DIRECTION_OUTPUT);
	/*主机拉高*/
	uapi_gpio_set_val(g_dht11_pin, GPIO_LEVEL_HIGH);

	/* 对数据进行处理 */
	humi_temp = DHT11_Data->humi_high8bit * 100 + DHT11_Data->humi_low8bit;
	DHT11_Data->humidity = (float)humi_temp / 100;

	humi_temp = DHT11_Data->temp_high8bit * 100 + DHT11_Data->temp_low8bit;
	DHT11_Data->temperature = (float)humi_temp / 100;

	/*检查读取的数据是否正确*/
	temp = DHT11_Data->humi_high8bit + DHT11_Data->humi_low8bit + DHT11_Data->temp_high8bit + DHT11_Data->temp_low8bit;
	if (DHT11_Data->check_sum == temp)
	{
		return ERRCODE_SUCC;
	}
	else
	{
#ifdef BSP_DHT11_DEBUG_LOG_OPEN
		osal_printk("%s DHT11 Data verification is abnormal.\r\n", BSP_DHT11_LOG);
#endif // BSP_DHT11_DEBUG_LOG_OPEN

		return ERRCODE_FAIL;
	}
}
